package com.kinyx.framework.bean;

import java.io.File;

import com.kinyx.framework.Configs;
import com.kinyx.framework.annotation.assist.CanNotNull;
import com.kinyx.framework.annotation.assist.CanNull;
import com.kinyx.framework.annotation.assist.Note;
import com.kinyx.framework.annotation.assist.ReturnNotNull;
import com.kinyx.framework.annotation.assist.ReturnNull;
import com.kinyx.framework.annotation.assist.Tester;
import com.kinyx.framework.annotation.assist.Throws;
import com.kinyx.framework.constraints.BASE64;
import com.kinyx.framework.file.KFileUtils;
import com.kinyx.framework.file.KPathUtils;
import com.kinyx.framework.image.KImageUtils;
import com.kinyx.framework.utils.KMatcherUtils;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

/**
 * 图片对象
 */
@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Tester("com.kinyx.framework.bean.KImageInfo_Tester_")
public class KImageInfo {

	/** 图片格式 */
	private static final String JPEG = ".JPEG";

	/** 文件服务器根地址 */
	private String rootUrl;
	/** 文件服务器根路径 */
	private String rootPath;
	/** 存储目录 */
	private String category;

	/** BASE64 */
	private String base64;
	/** 绝对路径 */
	private String absolutePath;
	/** 相对路径（去除文件服务器根路径的相对路径） */
	private String relativePath;
	/** 相对路径（去除文件服务器根路径及存储目录的相对路径） */
	private String relativePath2;
	/** 访问地址 */
	private String url;

	/**
	 * 构造方法
	 * @param rootUrl 文件服务器根地址
	 * @param rootPath 文件服务器根路径
	 * @param category 存储目录
	 * @return 新对象
	 */
	@ReturnNotNull
	public static KImageInfo of(@CanNotNull String rootUrl, @CanNotNull String rootPath, @CanNotNull String category) {
		final KImageInfo image = new KImageInfo();

		if (rootUrl == null) { rootUrl = Configs.fileServer_rootUrl; }
		image.rootUrl = KPathUtils.toPath(rootUrl);

		if (rootPath == null) { rootPath = Configs.fileServer_rootPath; }
		image.rootPath = KPathUtils.toPath(rootPath);

		if (category == null) { category = "/category"; }
		image.category = KPathUtils.toPath(category);

		return image;
	}

	/**
	 * 构造方法
	 * @param category 存储目录
	 * @return 新对象
	 */
	@ReturnNotNull
	public static KImageInfo of(@CanNotNull final String category) {
		return of(null, null, category);
	}

	/**
	 * 设置路径
	 * @param URL_or_BASE64 URL或BASE64
	 * @return 本对象
	 */
	@ReturnNotNull
	@Note("return this")
	@Throws(at = "KFileUtils.checkDirectory(File, boolean)", value = "ErrorCode0.IsNotADirectory", when = "!File.isDirectory()")
	@Throws(at = "KFileUtils.createNewFile(String)", value = "IOException", when = "File.createNewFile()")
	public KImageInfo with(@CanNotNull final String URL_or_BASE64) {
		if (KMatcherUtils.matches(URL_or_BASE64, BASE64.REGEX)) {// BASE64
			this.base64 = URL_or_BASE64;
			this.absolutePath = KPathUtils.toPath(KFileUtils.createNewFile(this.rootPath + this.category, JPEG).getAbsolutePath());// 在服务器上新建图片，并取路径。
			this.relativePath = this.absolutePath.substring(this.rootPath.length());
			this.relativePath2 = this.relativePath.substring(this.category.length());
			this.url = this.rootUrl + this.relativePath;
		} else {
			if (KPathUtils.isURL(URL_or_BASE64)) {
				// 绝对地址
				if (URL_or_BASE64.startsWith(this.rootUrl)) {// 指定服务器地址
					// 转换为相对路径
					this.relativePath = URL_or_BASE64.substring(this.rootUrl.length());
					this.relativePath2 = this.relativePath.substring(this.category.length());
					this.absolutePath = this.rootPath + this.relativePath;
					this.url = this.rootUrl + this.relativePath;
				} else {// 其它服务器地址
					// 不做路径处理
					this.url = URL_or_BASE64;
				}
			} else if (KPathUtils.isWindowsPath(URL_or_BASE64)) {
				// Windows全路径
				this.absolutePath = URL_or_BASE64;
				this.relativePath = this.absolutePath.substring(this.rootPath.length());
				this.relativePath2 = this.relativePath.substring(this.category.length());
				this.url = this.rootUrl + this.relativePath;
			} else if (URL_or_BASE64.startsWith("/")) {
				// 相对路径
				this.relativePath = URL_or_BASE64;
				this.relativePath2 = this.relativePath.substring(this.category.length());
				this.absolutePath = this.rootPath + this.relativePath;
				this.url = this.rootUrl + this.relativePath;
			}
		}
		return this;
	}

	/**
	 * 创建图片
	 * @param newThread 是否开启线程
	 * @return 本对象
	 */
	@ReturnNotNull
	@Note("return this")
	@Throws(at = "KFileUtils.checkDirectory(File, boolean)", value = "ErrorCode0.IsNotADirectory", when = "!File.isDirectory()")
	@Throws(at = "KFileUtils.createNewFile(String)", value = "IOException", when = "File.createNewFile()")
	@Throws(at = "KFileUtils.checkWriteable(File)", value = "ErrorCode0.FileIsNotWriteable", when = "!File.canWrite()")
	//	@Throws(at = "KImageUtils.write(String, byte[])", value = "FileNotFoundException", when = "new FileOutputStream(File)")
	@Throws(at = "KImageUtils.write(String, byte[])", value = "IOException", when = "OutputStream.write(byte[])")
	public KImageInfo create(@CanNotNull final boolean newThread) {
		if (this.base64 != null) {
			if (newThread) {
				// 开启线程执行文件的创建
				new Thread(() -> { KImageUtils.write(this.absolutePath, this.base64); }).start();
			} else {
				// 不开启线程执行文件的创建
				KImageUtils.write(this.absolutePath, this.base64);
			}
		}
		return this;
	}

	/**
	 * 取BASE64编码
	 * @param charset 字符集
	 * @return BASE64编码
	 */
	@ReturnNull("(this.base64 == null) && (this.absolutePath == null)")
	@Throws(at = "KBase64.encode(byte[], String)", value = "UnsupportedEncodingException", when = "new String(byte[], String)")
	@Throws(at = "KReader.readBytes(InputStream)", value = "IOException", when = "InputStream.available()")
	@Throws(at = "KReader.readBytes(InputStream)", value = "IOException", when = "InputStream.read(byte[])")
	@Throws(at = "KFileUtils.checkFile(File)", value = "ErrorCode0.FileDoesNotExists", when = "!File.exists()")
	@Throws(at = "KFileUtils.checkFile(File)", value = "ErrorCode0.IsNotAFile", when = "!File.isFile()")
	@Throws(at = "KFileUtils.checkReadable(File)", value = "ErrorCode0.FileIsNotReadable", when = "!File.canRead()")
	//	@Throws(at = "KFileReader.newFileInputStream(File)", value = "FileNotFoundException", when = "new FileInputStream(File)")
	public String getBase64(@CanNull(defaultValue = "Configs.default_KImageUtils_charset") final String charset) {
		if ((this.base64 == null) && (this.absolutePath == null)) {
			return null;
		}
		return KImageUtils.readBASE64(new File(this.absolutePath), charset);
	}

	@ReturnNull("(this.base64 == null) && (this.absolutePath == null)")
	@Throws(at = "KBase64.encode(byte[], String)", value = "UnsupportedEncodingException", when = "new String(byte[], String)")
	@Throws(at = "KReader.readBytes(InputStream)", value = "IOException", when = "InputStream.available()")
	@Throws(at = "KReader.readBytes(InputStream)", value = "IOException", when = "InputStream.read(byte[])")
	@Throws(at = "KFileUtils.checkFile(File)", value = "ErrorCode0.FileDoesNotExists", when = "!File.exists()")
	@Throws(at = "KFileUtils.checkFile(File)", value = "ErrorCode0.IsNotAFile", when = "!File.isFile()")
	@Throws(at = "KFileUtils.checkReadable(File)", value = "ErrorCode0.FileIsNotReadable", when = "!File.canRead()")
	//	@Throws(at = "KFileReader.newFileInputStream(File)", value = "FileNotFoundException", when = "new FileInputStream(File)")
	public String getBase64() {
		return this.getBase64(null);
	}

}
